#include <iostream>
#include <string>
#include "Sock.hpp"
#include "log.hpp"
using namespace std;

#define BITS 8
#define NUM sizeof(fd_set)*BITS
#define FD_NONE -1

class SelectServer
{
public:
    // 构造函数：初始化监听套接字，并绑定到指定端口。
    SelectServer(const uint16_t& port = 8080): _port(port)
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);
        logMessage(DEBUG, "%s", "create base socket success.");
        // 初始化_fd_array
        for(int i = 0; i < NUM; ++i) _fd_array[i] = FD_NONE;
        _fd_array[0] = _listensock;
    }
    
    // 析构函数：关闭监听套接字。
    ~SelectServer()
    {
        if(_listensock != FD_NONE)
        {
            close(_listensock);
            logMessage(DEBUG, "%s", "close base socket success.");
        }
    }

    // Start() 函数：服务器的主循环，使用 select() 函数监听所有的文件描述符，一旦有事件发生，就调用相应的处理函数。
    void Start()
    {
        DebugPrint();

        while(true)
        {
            fd_set rfds; //一组文件描述符的集合
            FD_ZERO(&rfds); // 清空

            int maxfd = _listensock; // 最大文件描述符值
            for(int i = 0; i < NUM; ++i)
            {
                if(_fd_array[i] == FD_NONE) continue; // 无效文件描述符，继续
                FD_SET(_fd_array[i], &rfds); // 添加到rfds
                if(maxfd < _fd_array[i]) maxfd = _fd_array[i]; // 更新maxfd
            }
            int n = select(maxfd + 1, &rfds, NULL, NULL, NULL); // 监视文件描述符可读性
            switch(n)
            {
            case 0:
                logMessage(DEBUG, "%s", "select timeout.");
                break;
            case -1:
                logMessage(WARNING, "select error %d : %s", errno, strerror(errno));
                break;
            default: // 有事件发生，执行操作：
                logMessage(DEBUG, "select success: get a new link event.");
                HandlerEvent(rfds);
                break;
            }
        }
    }

private:
    void HandlerEvent(const fd_set& rfds)
    {
        for(int i = 0; i < NUM; ++i) // rfds中可能存在重复的sock
        {
            // 去掉无效sock
            if(_fd_array[i] == FD_NONE) continue;
            // 
            if(FD_ISSET(_fd_array[i], &rfds))
            {
                // 此时当前的_fd_array[i]的读事件就绪
                if(_fd_array[i] == _listensock) Accepter(); // 该fd是监听套接字，有新的连接请求需要处理
                else Recver(i); // 该fd已就绪，有数据可读，执行读操作
            }
        }
    }

    void Accepter()
    {
        string clientIp; // 客户端ip
        uint16_t clientPort; // 客户端端口
        int clientSock = Sock::Accept(_listensock, &clientIp, &clientPort); // 建立连接
        if(clientSock == -1)
        {
            logMessage(WARNING, "accept error %d : %s", errno, strerror(errno));
            return;
        }
        logMessage(DEBUG, "get a new link: [%s:%d], sock: %d.", clientIp.c_str(), clientPort, clientSock);
        // 直接read / recv可能会被阻塞（sock数据到来的时间未知）
        // 得到新连接时，将新的sock托管给select，用select检查sock上是否有新数据
        // 如果sock上有数据，select读事件就绪，select会进行通知，随后我们再读取，不会被阻塞
        int pos = 1;
        for(; pos < NUM; ++pos)
        {
            if(_fd_array[pos] == FD_NONE)   break;
        }
        if(pos == NUM) {
            logMessage(WARNING, "%s:%d", "select already full, can't accept new link.\n close: %d", clientIp.c_str(), clientPort, clientSock);
            close(clientSock);
        } else {
            _fd_array[pos] = clientSock;
        }

    }

    void Recver(int pos)
    {
        int& sock = _fd_array[pos];
        // 读事件就绪

        logMessage(DEBUG, "message in, get IO event: %d", sock);
        // 此时fd的数据一定是就绪的
        char buffer[1024];
        int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if(n > 0) { // 成功接收数据
            buffer[n] = 0;
            logMessage(NORMAL, "client[%d]# %s", sock, buffer);
        } else if (n == 0) { // 对端关闭连接
            logMessage(DEBUG, "client[%d] quit, me, too.", sock);
            close(sock);
            sock = FD_NONE;
        } else { // 读取失败
            logMessage(WARNING, "%d sock recv error, %d : %s", sock, errno, strerror(errno));
            close(sock);
            sock = FD_NONE;
        }
    }

    void DebugPrint() // 打印当前的fd信息
    {
        cout << "_fd_array[]: ";
        for(int i = 0; i < NUM; ++i)
        {
            if(_fd_array[i] == FD_NONE) continue;
            cout << _fd_array[i] << " ";
        }
        cout << endl;
    }

    uint16_t _port; // 端口号
    int _listensock; // 监听套接字
    int _fd_array[NUM]; // 文件描述符数组
};